package service

import (
	"app-box/api_error"
	"app-box/database"
	"app-box/database/repo"
	"app-box/models"
	"app-box/models/result"
	"app-box/models/tables"
	"app-box/resource"
	"app-box/tools"
	filestore "app-box/tools/FileStore"
	"fmt"
	"time"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type AppBoxService struct {
	retpo *repo.AppBoxRepo
}

func NewAppBoxService() *AppBoxService {
	var service = new(AppBoxService)
	service.retpo = repo.NewAppBoxRepo(database.DefaultDB)
	return service
}

func (service AppBoxService) UploadApp(UserID models.IDType, Platform, Name, PackageName, Version, BuildVersion, VersionTitle, UpdateLog string, DownloadURL, Icon filestore.FileString) models.IDType {
	service.retpo.Begain()
	defer service.retpo.RollbackIfNeed()
	Icon.SaveTempFiles()
	DownloadURL.SaveTempFiles()
	var appBox tables.AppBox
	var app = tables.App{
		Platform:     Platform,
		Name:         Name,
		PackageName:  PackageName,
		Version:      Version,
		BuildVersion: BuildVersion,
		VersionTitle: VersionTitle,
		UpdateLog:    UpdateLog,
		Icon:         Icon,
		UserID:       UserID,
		PackageUrl:   DownloadURL,
	}
	if Platform == "iOS" { // 生成plist
		userIDStr, err := UserID.ToString()
		if err != nil {
			panic(err)
		}
		now := time.Now()
		randomStr := tools.RandomString(10)
		timestamp := now.UnixNano()
		plist := resource.NewPlistStr(app.PackageUrl.ToString(), app.Icon.ToString(), app.PackageName, app.Version, app.Name)
		url := filestore.UploadString(plist, fmt.Sprintf("/%s/app/%s/%s/%s-%d-%s.plist", userIDStr, app.PackageName, app.BuildVersion, app.VersionTitle, timestamp, randomStr))
		app.PlistUrl = url
	}
	if err := service.retpo.Insert(&app); err != nil {
		panic(api_error.DataBaseError(err))
	}
	if err := service.retpo.GetAppBox(UserID, Platform, PackageName, &appBox); err != nil {
		if err != gorm.ErrRecordNotFound {
			panic(api_error.DataBaseError(err))
		}
		/// 没有创建 APP Box
		appBox = tables.AppBox{
			UserID:      UserID,
			PackageName: PackageName,
			Platform:    Platform,
		}
		if err := service.retpo.DB.Create(&appBox).Error; err != nil {
			panic(api_error.DataBaseError(err))
		}
	}
	service.retpo.Commit()
	return app.ID
}

func (service AppBoxService) GetAppListForUser(userID models.IDType) (appList []result.AppList) {
	if err := service.retpo.GetAppListByUserID(userID, &appList); err != nil {
		panic(api_error.DataBaseError(err))
	}
	return
}

func (service AppBoxService) GetAppListForPackage(userID models.IDType, platform, packageName string) (appBox result.AppBox) {
	var box tables.AppBox
	if err := service.retpo.GetAppBox(userID, platform, packageName, &box); err != nil {
		panic(api_error.DataBaseErrorOrNotFound(err))
	}
	appBox = result.AppBox{
		ID:          box.ID,
		CreatedAt:   box.CreatedAt,
		UpdatedAt:   box.UpdatedAt,
		UserID:      box.UserID,
		PackageName: box.PackageName,
		Platform:    box.Platform,
		CustomID:    box.CustomID,
		Password:    len(box.Password) != 0,
		Description: box.Description,
	}
	var appList []result.AppList
	if err := service.retpo.GetAppListByPackageName(userID, platform, packageName, &appList); err != nil {
		panic(api_error.DataBaseError(err))
	}
	appBox.AppList = appList
	return
}

// ID 可以是APP box 的ID 或 CustomID
func (service AppBoxService) GetAppBoxForID(ID, password string) (appBox result.AppBox) {
	var box tables.AppBox
	var appboxID, err = models.NewID(ID)
	if err == nil {
		if err := service.retpo.GetAppBoxByID(appboxID, &box); err != nil {
			panic(api_error.DataBaseErrorOrNotFound(err))
		}
	} else {
		if err := service.retpo.GetAppBoxByCustomID(ID, &box); err != nil {
			panic(api_error.DataBaseErrorOrNotFound(err))
		}
	}
	if len(box.Password) != 0 {
		if len(password) == 0 {
			panic(api_error.AppBoxPasswordRequireError())
		}
		if err := bcrypt.CompareHashAndPassword([]byte(box.Password), []byte(password)); err != nil {
			panic(api_error.AppBoxPasswordError())
		}
	}
	appBox = result.AppBox{
		ID:          box.ID,
		CreatedAt:   box.CreatedAt,
		UpdatedAt:   box.UpdatedAt,
		UserID:      box.UserID,
		PackageName: box.PackageName,
		Platform:    box.Platform,
		CustomID:    box.CustomID,
		Password:    len(box.Password) != 0,
		Description: box.Description,
	}
	if err := service.retpo.GetAppListByPackageName(appBox.UserID, appBox.Platform, appBox.PackageName, &appBox.AppList); err != nil {
		panic(api_error.DataBaseError(err))
	}
	return
}

func (service AppBoxService) UpdateAppBoxCustomID(userID models.IDType, appboxID models.IDType, CustomID string) {
	var appBox tables.AppBox
	if err := service.retpo.GetAppBoxByID(appboxID, &appBox); err != nil {
		panic(api_error.DataBaseErrorOrNotFound(err))
	}
	if appBox.UserID != userID {
		panic(api_error.PermissionError())
	}
	appBox.CustomID = models.NullString{
		String: CustomID,
		Valid:  len(CustomID) > 0,
	}
	if err := service.retpo.UpdateAppBoxCustomID(appboxID, CustomID); err != nil {
		panic(api_error.DataBaseError(err))
	}
}

func (service AppBoxService) UpdateAppBoxDescription(userID models.IDType, appboxID models.IDType, Description string) {
	var appBox tables.AppBox
	if err := service.retpo.GetAppBoxByID(appboxID, &appBox); err != nil {
		panic(api_error.DataBaseErrorOrNotFound(err))
	}
	if appBox.UserID != userID {
		panic(api_error.PermissionError())
	}
	appBox.Description = Description
	if err := service.retpo.UpdateAppBoxDescription(appboxID, Description); err != nil {
		panic(api_error.DataBaseError(err))
	}
}

func (service AppBoxService) UpdateAppBoxPassword(userID models.IDType, appboxID models.IDType, Password string) {
	var appBox tables.AppBox
	if err := service.retpo.GetAppBoxByID(appboxID, &appBox); err != nil {
		panic(api_error.DataBaseErrorOrNotFound(err))
	}

	if appBox.UserID != userID {
		panic(api_error.PermissionError())
	}

	if len(Password) > 0 {
		cryptPass, err := bcrypt.GenerateFromPassword([]byte(Password), bcrypt.DefaultCost)
		if err != nil {
			panic(api_error.PasswordCreateError(err))
		}
		appBox.Password = string(cryptPass)
	} else {
		appBox.Password = ""
	}

	if err := service.retpo.UpdateAppBoxPassword(appboxID, Password); err != nil {
		panic(api_error.DataBaseError(err))
	}
}
